package com.cmty256.common.aop;

import com.cmty256.common.utils.ClassUtil;
import com.cmty256.common.utils.JsonUtil;
import com.cmty256.common.utils.WebUtil;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 请求日志切面
 *
 * @author chenmeng
 */
@Aspect
@Component
public class RequestLogAspect {

    private static final Logger log = LoggerFactory.getLogger(RequestLogAspect.class);

    /**
     * 环绕通知：对Controller方法的请求和响应进行日志记录
     */
    @Around("execution(* com.cmty256.*.controller.*.*(..))")
    public Object aroundApi(ProceedingJoinPoint point) throws Throwable {
        // 获取请求信息
        HttpServletRequest request = WebUtil.getRequest();
        String requestUrl = (Objects.requireNonNull(request)).getRequestURI();
        String requestMethod = request.getMethod();

        // 初始化日志内容
        StringBuilder beforeReqLog = new StringBuilder(300);
        List<Object> beforeReqArgs = new ArrayList<>();
        beforeReqLog.append("\n============== Request Start ==============\n");
        beforeReqLog.append("Method:{} URL:{}");

        beforeReqArgs.add(requestMethod);
        beforeReqArgs.add(requestUrl);

        // 记录请求参数和请求头
        logMethodArguments(point, beforeReqLog, beforeReqArgs);
        logRequestHeaders(request, beforeReqLog, beforeReqArgs);
        beforeReqLog.append("============== Request End ==============\n");

        // 记录请求开始时间，用于性能分析
        long startNs = System.nanoTime();

        log.info(beforeReqLog.toString(), beforeReqArgs.toArray());

        // 执行目标方法并记录响应结果
        Object result = null;
        StringBuilder afterReqLog = new StringBuilder(200);
        List<Object> afterReqArgs = new ArrayList<>();
        afterReqLog.append("\n============== Response Start ==============\n");

        try {
            // 执行目标方法
            result = point.proceed();

            // 记录响应体
            afterReqLog.append("Response: {}\n");
            // 将响应结果转为JSON字符串并添加到日志中
            afterReqArgs.add(JsonUtil.toJson(result));
        } catch (Exception ex) {
            afterReqLog.append("请求处理过程中发生错误: {}\n");
            afterReqArgs.add(ex.getMessage());
            log.error("API执行过程中发生错误", ex);
        } finally {
            // 记录请求执行的时间
            long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
            afterReqLog.append("Method:{} URL:{} ({} ms)\n");
            afterReqArgs.add(requestMethod);
            afterReqArgs.add(requestUrl);
            afterReqArgs.add(tookMs);
            afterReqLog.append("============== Response End  ==============\n");

            // 记录响应日志
            log.info(afterReqLog.toString(), afterReqArgs.toArray());
        }

        return result;
    }

    /**
     * 记录方法的参数
     */
    private void logMethodArguments(ProceedingJoinPoint point, StringBuilder beforeReqLog, List<Object> beforeReqArgs) {
        MethodSignature ms = (MethodSignature) point.getSignature();
        Method method = ms.getMethod();
        Object[] args = point.getArgs();
        Map<String, Object> paraMap = new HashMap<>(16);
        Object requestBodyValue = null;

        // 处理每个参数，提取PathVariable、RequestBody等
        for (int i = 0; i < args.length; ++i) {
            MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
            PathVariable pathVariable = methodParam.getParameterAnnotation(PathVariable.class);
            if (pathVariable == null) {
                RequestBody requestBody = methodParam.getParameterAnnotation(RequestBody.class);
                String parameterName = methodParam.getParameterName();
                Object value = args[i];
                if (requestBody != null) {
                    requestBodyValue = value;
                } else {
                    processArgument(value, paraMap, parameterName);
                }
            }
        }

        // 如果有参数，记录参数内容
        if (!paraMap.isEmpty()) {
            beforeReqLog.append("Parameters: {}\n");
            beforeReqArgs.add(JsonUtil.toJson(paraMap));
        } else {
            beforeReqLog.append("\n");
        }

        // 如果请求体不为空，记录请求体内容
        if (requestBodyValue != null) {
            beforeReqLog.append("Body:{}\n");
            beforeReqArgs.add(JsonUtil.toJson(requestBodyValue));
        }
    }

    /**
     * 处理单个方法参数并添加到参数映射中
     */
    private void processArgument(Object value, Map<String, Object> paraMap, String parameterName) {
        if (value instanceof HttpServletRequest) {
            paraMap.putAll(((HttpServletRequest) value).getParameterMap());
        } else if (value instanceof WebRequest) {
            paraMap.putAll(((WebRequest) value).getParameterMap());
        } else if (value instanceof MultipartFile) {
            MultipartFile multipartFile = (MultipartFile) value;
            paraMap.put(parameterName, multipartFile.getOriginalFilename());
        } else if (value instanceof List) {
            List<?> list = (List<?>) value;
            for (Object item : list) {
                if (item instanceof MultipartFile) {
                    paraMap.put(parameterName, "Multipart文件数据");
                    break;
                }
            }
        } else {
            if (ClassUtils.isPrimitiveOrWrapper(value.getClass())) {
                paraMap.put(parameterName, value);
            } else if (JsonUtil.canSerialize(value)) {
                paraMap.put(parameterName, value);
            } else {
                paraMap.put(parameterName, "无法序列化的对象");
            }
        }
    }

    /**
     * 记录HTTP请求头信息
     */
    private void logRequestHeaders(HttpServletRequest request, StringBuilder beforeReqLog, List<Object> beforeReqArgs) {
        Enumeration<String> headers = request.getHeaderNames();
        while (headers.hasMoreElements()) {
            String headerName = headers.nextElement();
            String headerValue = request.getHeader(headerName);
            beforeReqLog.append("{}: {}\n");
            beforeReqArgs.add(headerName);
            beforeReqArgs.add(headerValue);
        }
    }
}
